package org.dei.perla.utils.messages;

/**
 * Classe contenente vari metodi statici, 
 * di utilità generale per la comunicazione tra componenti
 */
public final class MsgUtility {
	
	/**
	 * Flag per riconoscere il significato di un messaggio
	 */
	public static final int flagAck = 0;
	public static final int flagBinding = 1;
	public static final int flagNegativeAck = 2;
	public static final int flagOthers = 3;
	
	/**
	 * Offset dei vari tipi di identificatore del canale
	 */
	private static final int startFlagOffset = 0;
	private static final int startBvciOffset = 4;
	private static final int startVciOffsetAckMessage = 8;
	
	private static final int startVciOffset = 4;
	
	/**
	 * Valore che definisce quando inizia il dominio dei VCI e finisce quello dei BVCI
	 */
	protected static final int bindDomain = 2000000000;
	
	/**
	 * Numero di byte che compongono l'identificatore di canale virtuale
	 */
	public static final int channelIdLength = 4;
	
	public static final int flagLength = 4;
	
	/**
	 * Converte un intero in un array di 4 bytes
	 * 
	 * @param parNumber Un intero
	 * @return Restituisce un array di 4 byte
	 */
	public static byte []intToByteArray(int parNumber) {
			
		byte []b = new byte[4];
		
		b[0] = (byte)( parNumber >> 24 );
		b[1] = (byte)( (parNumber << 8) >> 24 );
		b[2] = (byte)( (parNumber << 16) >> 24 );
		b[3] = (byte)( (parNumber << 24) >> 24 );
		
		return b;
		
	}	
	
	/**
	 * Ritorna il flag del messaggio
	 * @param parPayload payload dal quale estrarre il flag
	 * @return il flag
	 */
	public static int getFlag(byte[] parPayload){
		
		return getIntFromPayload(parPayload , startFlagOffset, (flagLength + startFlagOffset));
		
	}
	
	/**
	 * Ritorna l'identificatore vci del messaggio di ack
	 * @param parPayload payload dal quale estrarre il vci
	 * @return il vci
	 */
	public static int getVciFromAckMessage(byte[] parPayload){
		
		return getIntFromPayload(parPayload, startVciOffsetAckMessage , (startVciOffsetAckMessage + channelIdLength));
		
	}
	
	/**
	 * Ritorna l'identificatore bvci
	 * @param parPayload payload dal quale estrarre il bvci
	 * @return il bvci
	 */
	public static int getBvci (byte[] parPayload){
		
		return getIntFromPayload(parPayload, startBvciOffset , (startBvciOffset + channelIdLength));
		
	}
	
	/**
	 * Ritorna l'identificatore vci di un normale messaggio
	 * @param parPayload payload dal quale estrarre il vci
	 * @return il vci
	 */
	public static int getVciNormalMessage (byte[] parPayload){
		
		return getIntFromPayload(parPayload, startVciOffset , (startVciOffset + channelIdLength));
		
	}

	/**
	 * Converte un array di 4 bytes nell'intero relativo
	 * 
	 * @param b Array di 4 byte
	 * @return Restituisce un intero
	 */
	public static int byteArrayToInt(byte[] b) {
			
		int value = 0;
		
		for (int i = 0; i < 4; i++) {
				
			int shift = (4 - 1 - i) * 8;
			value += (b[i] & 0x000000FF) << shift;
		
		}
		
		return value;
		
	}

	/**
	 * Estrae un intero dal payload, leggendo in seguenza i byte 
	 * nell'intervallo fornito come parametro
	 * 
	 * @param payload, payload da analizzare
	 * @param start, inizio dell'intervallo (compreso)
	 * @param end, fine dell'intervallo (escluso)
	 * @return l'intero estratto
	 */
	public static int getIntFromPayload (byte[] payload, int start, int end) {
		
		byte[] segment = new byte[end - start];
		int j = 0;
		
		for (int i = start; i < end; i++) {
			segment[j++] = payload[i];
		}
		
		return MsgUtility.byteArrayToInt(segment);
	}
	
	/**
	 * Crea un payload con ACK + BVCI + VCI da mandare con il messaggio di ack
	 * @param parBvci bvci
	 * @param parVci nuovo identificatore del canale
	 * @return payload
	 */
	public static byte[] createAckPayload(int parBvci, int parVci){
		
		byte[] tBvciByte = MsgUtility.intToByteArray(parBvci);
		byte[] tVciByte = MsgUtility.intToByteArray(parVci);
		byte[] tFlag = MsgUtility.intToByteArray(flagAck);
		
		byte[] tPayload = new byte[tBvciByte.length + tVciByte.length + tFlag.length];
		
		for(int i = 0; i < tBvciByte.length + tVciByte.length + tFlag.length; i++){
			
			if(i < tFlag.length)
				tPayload[i] = tFlag[i];
			
			else if (i < (tFlag.length + tBvciByte.length))
				tPayload[i] = tBvciByte[i - tFlag.length];
			
			else
				tPayload[i] = tVciByte[i - (tFlag.length + tBvciByte.length)];
		}
		
		return tPayload;
		
	}
	
	/**
	 * Crea un payload con ACK + BVCI da mandare con il messaggio di ack negativo
	 * @param parBvci bvci
	 * @return payload
	 */
	public static byte[] createNegativeAckPayload(int parBvci){
		
		byte[] tBvciByte = MsgUtility.intToByteArray(parBvci);
		
		byte[] tFlag = MsgUtility.intToByteArray(flagNegativeAck);
		
		byte[] tPayload = new byte[tBvciByte.length + tFlag.length];
		
		for(int i = 0; i < tBvciByte.length + tFlag.length; i++){
			
			if(i < tFlag.length)
				tPayload[i] = tFlag[i];
			
			else 
				tPayload[i] = tBvciByte[i - tFlag.length];
			
		}
		
		return tPayload;
		
	}
	
	public static byte[] modifiedBvciBindingMessage(byte[] parPayload, int parBvci, int parRemappedBvci){
		
		byte[] tPayload = parPayload;
		
		byte[] tRemappedBvciByte = MsgUtility.intToByteArray(parRemappedBvci);
		
		for(int i = 0; i < tRemappedBvciByte.length; i++){
			
			tPayload[i + startBvciOffset] = tRemappedBvciByte[i];
			
		}
		
		return tPayload;
	}
	
	// Anche se c'e' Normal funziona anche per i messaggi di binding, in quanto l'offset del flag di binding
	// e' lo stesso del flag di vci normale
	public static byte[] getNormalMsgContent(byte[] parPayload){
		
		int contentLength = parPayload.length - (MsgUtility.flagLength + MsgUtility.channelIdLength);
		byte[] tPayload = new byte[contentLength];
		for(int i = 0; i < contentLength; i++){
			
			tPayload[i] = parPayload[i + (MsgUtility.flagLength + MsgUtility.channelIdLength)];
			
		}
		
		return tPayload;
	}
	
}
